/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package chatsystem;

import chatsystem.observable.FileTransfertSignal;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Observable;
import java.util.logging.Level;
import java.util.logging.Logger;
import protocol.Port;

/**
 *
 * @author loucif-d
 */
public class FileNI extends Observable implements Runnable {

    private ServerSocket servSock;
    private ListeUtilisateurs users;
    private HashMap<InetAddress, File> notYetAcceptedFiles;
    private HashMap<InetAddress, File> awaitedFiles;
    private HashMap<InetAddress, File> pendingSending;
    private HashMap<InetAddress, FileTransfertSignal> signals;

    public FileNI(ListeUtilisateurs users) throws IOException {
        this.servSock = new ServerSocket(Port.receptionTCP);
        this.awaitedFiles = new HashMap<InetAddress, File>();
        this.pendingSending = new HashMap<InetAddress, File>();
        this.notYetAcceptedFiles = new HashMap<InetAddress, File>();
        this.signals = new HashMap<InetAddress, FileTransfertSignal>();
        this.users = users;

    }

    public void addSending(InetAddress dest, File file) {
        this.pendingSending.put(dest, file);
        
        this.signals.put(dest,
                new FileTransfertSignal(users.getUserByAddr(dest).getUserName(),
                file.getName(),
                FileTransfertSignal.SENDING,
                FileTransfertSignal.ADDED));

        this.setChanged();
        this.notifyObservers(this.signals.get(dest));
        System.out.println(file.getName()+" est ajouté à la liste des envois");
    }

    public void startSending(InetAddress dest) throws IOException {
        this.signals.put(dest, this.signals.get(dest).setStarted());
        this.setChanged();
        this.notifyObservers(this.signals.get(dest));
        
        this.sendFile(dest, this.pendingSending.get(dest));
        
        System.out.println(this.pendingSending.get(dest).getName()+" a été envoyé");
        this.signals.remove(dest);
        this.pendingSending.remove(dest);
    }

    public void removeSending(InetAddress dest) {
        this.signals.put(dest, this.signals.get(dest).setRemoved());
        this.setChanged();
        this.notifyObservers(this.signals.get(dest));
        
        System.out.println(this.pendingSending.get(dest).getName()+" : envoi annulé");
        this.signals.remove(dest);
        this.pendingSending.remove(dest);
    }

    public void addReceiving(InetAddress emet, File file) {
        this.notYetAcceptedFiles.put(emet, file);
        this.signals.put(emet,
                new FileTransfertSignal(users.getUserByAddr(emet).getUserName(),
                file.getName(),
                FileTransfertSignal.RECEIVING,
                FileTransfertSignal.ADDED));
        this.setChanged();
        this.notifyObservers(this.signals.get(emet));
        System.out.println(file.getName()+" : réception en attente d'etre accepté");
    }

    public void startReceiving(InetAddress emet) throws IOException {
        this.awaitedFiles.put(emet, this.notYetAcceptedFiles.remove(emet));
        this.receiveFile();

        this.signals.put(emet, this.signals.get(emet).setStarted());
        this.setChanged();
        this.notifyObservers(this.signals.get(emet));
        System.out.println(this.awaitedFiles.get(emet).getName()+" : en cours de réception");
    }

    public void removeReceiving(InetAddress emet) {
        this.signals.put(emet, this.signals.get(emet).setRemoved());
        this.setChanged();
        this.notifyObservers(this.signals.get(emet));
        //TODO
        System.out.println(this.notYetAcceptedFiles.get(emet).getName()+" : réception refusée");
        this.signals.remove(emet);
        this.notYetAcceptedFiles.remove(emet);
    }

    public void sendFile(InetAddress addr, File f) throws IOException {
        FileTransfertSignal sig;
        Socket s = new Socket(addr, Port.receptionTCP);
        OutputStream socketOS = s.getOutputStream();
        FileInputStream fileIS = new FileInputStream(f);

        this.signals.put(addr, this.signals.get(addr).setProgress(0));
        this.setChanged();
        this.notifyObservers(this.signals.get(addr));

        int b;
        while ((b = fileIS.read()) != -1) {
            socketOS.write(b);
        }

        socketOS.flush();
        socketOS.close();
        s.close();
        fileIS.close();

        this.signals.put(addr, this.signals.get(addr).setProgress(100));
        this.setChanged();
        this.notifyObservers(this.signals.get(addr));
    }

    public void receiveFile() throws IOException {
        new Thread(this).start();
    }

    public void run() {
        try {
            Socket sock = servSock.accept();
            InputStream socketIS = sock.getInputStream();
            InetAddress addr = sock.getInetAddress();
            File f = this.awaitedFiles.get(addr);
            //System.out.println("true si fichier créé "+f.createNewFile());
            
            //System.out.println("true si droit d'écriture"+f.setWritable(true));
            
            //System.out.println("chemin fu fichier : "+f.getPath());
            //crée un fileOS avec le premier fichier de la liste des fichiers attendus
            FileOutputStream fileOS = new FileOutputStream(f);

            this.signals.put(addr, this.signals.get(addr).setProgress(0));
            this.setChanged();
            this.notifyObservers(this.signals.get(addr));

            int b;
            while ((b = socketIS.read()) != -1) {
                fileOS.write(b);
            }

            fileOS.flush();
            fileOS.close();
            socketIS.close();
            sock.close();

            this.signals.put(addr, this.signals.get(addr).setProgress(100));
            this.setChanged();
            this.notifyObservers(this.signals.get(addr));
            
            System.out.println(f.getName()+" : réception terminée");
            this.signals.remove(addr);
            this.awaitedFiles.remove(addr);
            
        } catch (IOException ex) {
            Logger.getLogger(FileNI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
